-- 32 bit version alu
-- bowmanb

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use work.typeDefinitions.all;

entity alu is 
  port (opcode   : in  aluOp;  
        uns      : in  std_logic;
        A, B     : in  std_logic_vector (31 downto 0);
        output   : out std_logic_vector (31 downto 0);
        negative : out std_logic;
        overflow : out std_logic;
        zero     : out std_logic);
end alu;

architecture arch of alu is

  signal r_add : std_logic_vector(31 downto 0);
  signal r_sub : std_logic_vector(31 downto 0);
  signal r_sll : std_logic_vector(31 downto 0);
  signal r_srl : std_logic_vector(31 downto 0);
  signal r_and : std_logic_vector(31 downto 0);
  signal r_or  : std_logic_vector(31 downto 0);
  signal r_xor : std_logic_vector(31 downto 0);
  signal r_nor : std_logic_vector(31 downto 0);  -- op reults
  signal r_slt : std_logic_vector(31 downto 0);

  signal S  : std_logic_vector(31 downto 0);
  signal o  : std_logic_vector(31 downto 0);
  signal ov : std_logic;
  signal lt : std_logic;

  signal Cin : std_logic;

  component aluCmp is
    
    port (
      A        : in  std_logic_vector(31 downto 0);
      B        : in  std_logic_vector(31 downto 0);
      uns      : in  std_logic;
      lessthan : out std_logic);  

  end component;

  component adder
    port (
      Cin      : in  std_logic;
      A        : in  std_logic_vector(31 downto 0);
      B        : in  std_logic_vector(31 downto 0);
      Sum      : out std_logic_vector(31 downto 0);
      Overflow : out std_logic);
  end component;

  component BarrelShifter
    port (
      A   : in  std_logic_vector(31 downto 0);
      B   : in  std_logic_vector(31 downto 0);
      O_R : out std_logic_vector(31 downto 0);
      O_L : out std_logic_vector(31 downto 0));
  end component;

begin  -- arch

  aluCmp_c : aluCmp port map (
    A        => A,
    B        => B,
    uns      => uns,
    lessthan => lt);

  r_slt(31 downto 1) <= (others => '0');
  r_slt(0)           <= lt;

  add_c : adder port map (
    Cin => Cin, A => A, B => B, Sum => S, overflow => ov);

  shift_c : BarrelShifter port map (
    A, B, r_srl, r_sll);
  
  Cin <= '1' when (opcode = op_sub) else
         '0';

  output <= o;
  -- purpose: add process
  -- type   : combinational
  -- inputs : A, B, opcode
  -- outputs: 
  compute : process (A, B, S)

  begin  -- process add_p
    r_add <= S;
    r_sub <= S;
    r_or  <= A or B;
    r_and <= A and B;
    r_xor <= A xor B;
    r_nor <= A nor B;
  end process compute;

  mux : process (opcode, r_add, r_sub, r_or, r_and, r_nor, r_xor, r_sll, r_srl, r_slt)
  begin  -- process mux 
    -- purpose: and op
    -- type   : combinational
    -- inputs : (A,B)
    -- outputs: 

    case opcode is
      when op_sll =>
        o <= r_sll;
      when op_srl =>
        o <= r_srl;
      when op_add =>
        o <= r_add;
      when op_sub =>
        o <= r_sub;
      when op_and =>
        o <= r_and;
      when op_nor =>
        o <= r_nor;
      when op_or =>
        o <= r_or;
      when op_xor =>
        o <= r_xor;
      when op_slt =>
        o <= r_slt;
      when others =>
        o <= (others => '0');
    end case;
  end process mux;

  flags : process (o, opcode, uns, ov)
  begin  -- process flags
    if(o(31) = '1') then
      negative <= '1';
    else
      negative <= '0';
    end if;

    if(o = (o'range => '0')) then
      zero <= '1';
    else
      zero <= '0';
    end if;

    if(opcode =  op_add or opcode = op_sub) then
      if(uns = '0') then
        overflow <= ov;
      else
        overflow <= '0';
      end if;
    else
      overflow <= '0';
    end if;

  end process flags;


end arch;
